package com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util;

import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.reqrespobj.ReqRespHeader;
import com.fingard.text.StringHelper;

import static com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.Constants.UNIONPAY_CNNAME;

/**
 * @ClassName: CertUtil
 * @Description: 证书工具类，主要用于对证书的加载和使用
 */
public class CertUtil extends DirectBase {

    /**
     * 证书容器，存储对商户请求报文签名私钥证书.
     */
    private KeyStore keyStore = null;
    public ActSetItem actItem = null;
    /**
     * 验证银联返回报文签名的公钥证书存储Map.
     */
    private Map<String, X509Certificate> certMap = new HashMap<String, X509Certificate>();
    /**
     * 验证银联返回报文签名证书.
     */
    private X509Certificate validateCert = null;

    public CertUtil(String p_act, ReqRespHeader p_reqHeader, StringBuilder p_sb) throws Exception {
        reqHeader = p_reqHeader;
        sbLog = p_sb;
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        actItem = this.getBankConfig().getActSetItem(p_act, "");
        if (actItem != null) {
            keyStore = getKeyInfo(actItem.ownKeyStorePath, actItem.ownKeyPassword, "PKCS12");
            initValidateCertFromDir();
        }
    }

    /**
     * 用配置文件acp_sdk.properties配置路径 加载验证签名证书
     */
    private void initValidateCertFromDir() {
        certMap.clear();
        String dir = actItem.ownCertFilePath;
        WriteBankLogLn("加载验证签名证书目录==>" + dir + " 注：如果请求报文中version=5.1.0那么此验签证书目录使用不到，可以不需要设置（version=5.0.0必须设置）。");
        if (StringHelper.isNullOrEmpty(dir)) {
            WriteBankLogLn("WARN: ownCertFilePath is empty");
            return;
        }
        CertificateFactory cf = null;
        FileInputStream in = null;
        try {
            cf = CertificateFactory.getInstance("X.509", "BC");
        } catch (NoSuchProviderException e) {
            WriteBankLogLn("LoadVerifyCert Error: No BC Provider", e);
            return;
        } catch (CertificateException e) {
            WriteBankLogLn("LoadVerifyCert Error", e);
            return;
        }
        File fileDir = new File(dir);
        File[] files = fileDir.listFiles(new CerFilter());
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            try {
                in = new FileInputStream(file.getAbsolutePath());
                validateCert = (X509Certificate) cf.generateCertificate(in);
                if (validateCert == null) {
                    WriteBankLogLn("Load verify cert error, " + file.getAbsolutePath() + " has error cert content.");
                    continue;
                }
                certMap.put(validateCert.getSerialNumber().toString(),
                        validateCert);
                // 打印证书加载信息,供测试阶段调试
                WriteBankLogLn("[" + file.getAbsolutePath() + "][CertId="
                        + validateCert.getSerialNumber().toString() + "]");
            } catch (CertificateException e) {
                WriteBankLogLn("LoadVerifyCert Error", e);
            } catch (FileNotFoundException e) {
                WriteBankLogLn("LoadVerifyCert Error File Not Found", e);
            } finally {
                if (null != in) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        WriteBankLogLn(e.toString());
                    }
                }
            }
        }
        WriteBankLogLn("LoadVerifyCert Finish");
    }

    /**
     * 将签名私钥证书文件读取为证书存储对象
     *
     * @param pfxkeyfile 证书文件名
     * @param keypwd     证书密码
     * @param type       证书类型
     * @return 证书对象
     * @throws IOException
     */
    public KeyStore getKeyInfo(String pfxkeyfile, String keypwd, String type) throws IOException {
        WriteBankLogLn("加载签名证书==>" + pfxkeyfile);
        FileInputStream fis = null;
        try {
            KeyStore ks = KeyStore.getInstance(type, "BC");
            WriteBankLogLn("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=[" + keypwd + "],type=[" + type + "]");
            fis = new FileInputStream(pfxkeyfile);
            char[] nPassword = null;
            nPassword = null == keypwd || "".equals(keypwd.trim()) ? null : keypwd.toCharArray();
            if (null != ks) {
                ks.load(fis, nPassword);
            }
            return ks;
        } catch (Exception e) {
            WriteBankLogLn("getKeyInfo Error", e);
            //e.printStackTrace();
            return null;
        } finally {
            if (null != fis)
                fis.close();
        }
    }

    /**
     * 获取配置文件acp_sdk.properties中配置的签名私钥证书certId
     *
     * @return 证书的物理编号
     */
    public String getSignCertId() {
        try {
            Enumeration<String> aliasenum = keyStore.aliases();
            String keyAlias = null;
            if (aliasenum.hasMoreElements()) {
                keyAlias = aliasenum.nextElement();
            }
            X509Certificate cert = (X509Certificate) keyStore
                    .getCertificate(keyAlias);
            return cert.getSerialNumber().toString();
        } catch (Exception e) {
            WriteBankLogLn("getSignCertId Error", e);
            return null;
        }
    }

    /**
     * 通过keyStore 获取私钥签名证书PrivateKey对象
     *
     * @return
     */
    public PrivateKey getSignCertPrivateKey() {
        try {
            Enumeration<String> aliasenum = keyStore.aliases();
            String keyAlias = null;
            if (aliasenum.hasMoreElements()) {
                keyAlias = aliasenum.nextElement();
            }
            PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias,
                    actItem.ownKeyPassword.toCharArray());
            return privateKey;
        } catch (KeyStoreException e) {
            WriteBankLogLn("getSignCertPrivateKey Error", e);
            return null;
        } catch (UnrecoverableKeyException e) {
            WriteBankLogLn("getSignCertPrivateKey Error", e);
            return null;
        } catch (NoSuchAlgorithmException e) {
            WriteBankLogLn("getSignCertPrivateKey Error", e);
            return null;
        }
    }

    /**
     * 通过certId获取验签证书Map中对应证书PublicKey
     *
     * @param certId 证书物理序号
     * @return 通过证书编号获取到的公钥
     */
    public PublicKey getValidatePublicKey(String certId) {
        X509Certificate cf = null;
        if (certMap.containsKey(certId)) {
            // 存在certId对应的证书对象
            cf = certMap.get(certId);
            return cf.getPublicKey();
        } else {
            // 不存在则重新Load证书文件目录
            initValidateCertFromDir();
            if (certMap.containsKey(certId)) {
                // 存在certId对应的证书对象
                cf = certMap.get(certId);
                return cf.getPublicKey();
            } else {
                WriteBankLogLn("缺少certId=[" + certId + "]对应的验签证书.");
                return null;
            }
        }
    }

    /**
     * 将字符串转换为X509Certificate对象.
     *
     * @param x509CertString
     * @return
     */
    public X509Certificate genCertificateByStr(String x509CertString) {
        X509Certificate x509Cert = null;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
            InputStream tIn = new ByteArrayInputStream(
                    x509CertString.getBytes("ISO-8859-1"));
            x509Cert = (X509Certificate) cf.generateCertificate(tIn);
        } catch (Exception e) {
            WriteBankLogLn("gen certificate error", e);
        }
        return x509Cert;
    }

    /**
     * 检查证书链
     *
     * @param cert 待验证的证书
     * @return
     */
    public boolean verifyCertificate(X509Certificate cert) {

        if (null == cert) {
            WriteBankLogLn("cert must Not null");
            return false;
        }
        try {
            cert.checkValidity();//验证有效期
        } catch (Exception e) {
            WriteBankLogLn("verifyCertificate fail", e);
            return false;
        }

        // 验证公钥是否属于银联
        if (!UNIONPAY_CNNAME.equals(getIdentitiesFromCertficate(cert))) {
            WriteBankLogLn("cer owner is not CUP:" + getIdentitiesFromCertficate(cert));
            return false;
        }

        return true;
    }

    /**
     * 获取证书的CN
     *
     * @param aCert
     * @return
     */
    private String getIdentitiesFromCertficate(X509Certificate aCert) {
        String tDN = aCert.getSubjectDN().toString();
        String tPart = "";
        if ((tDN != null)) {
            String tSplitStr[] = tDN.substring(tDN.indexOf("CN=")).split("@");
            if (tSplitStr != null && tSplitStr.length > 2
                    && tSplitStr[2] != null)
                tPart = tSplitStr[2];
        }
        return tPart;
    }

    /**
     * 证书文件过滤器
     */
    static class CerFilter implements FilenameFilter {
        public boolean isCer(String name) {
            if (name.toLowerCase().endsWith(".cer")) {
                return true;
            } else {
                return false;
            }
        }

        public boolean accept(File dir, String name) {
            return isCer(name);
        }
    }
}
